import { z } from "zod";
import prisma from "../prisma_client";
import { Prisma } from "@prisma/client";
import { convertNullToUndefined } from "~/utils";

export const zRole = z.object({
  id: z.string().transform((v) => parseInt(v)).optional(),
  name: z.string(),
  zh_name: z.string().optional(),

  permissionIds: z.string()
    .transform((v) => JSON.parse(v).map(Number))
    .refine((arr) => Array.isArray(arr) && arr.every(item => typeof item === 'number'), {
      message: "permissionIds must be an array of numbers"
    })
    .transform((arr) => new Set(arr))
    .optional(),
  users: z.array(z.lazy(() => z.object(
    {
      id: z.string(),
      username: z.string().optional(),
      nickname: z.string().optional(),
      avatar: z.string().optional()
    }
  ))).optional()
});

export type RoleModel = z.infer<typeof zRole>;

export async function findAllRoles() {
  const roles = await prisma.role.findMany({
    where: {
      status: 1,
    },
    include: {
      permissions: true,
      users: {
        include: {
          user: true
        }
      },
    },
  });

  return toRoleModel(roles);
}

function toRoleModel(
  roles: Prisma.RoleGetPayload<{
    include: {
      permissions: true;
      users: {
        include: {
          user: true;
        }
      }
    }
  }>[]): RoleModel[] {
  return roles.map((role) => {
    return {
      ...convertNullToUndefined(role),
      permissionIds: new Set(role.permissions.map((permission) => permission.permission_id)),
      users: role.users.map(user => {
        return {
          ...convertNullToUndefined(user.user),
        }
      })
    };
  });
}

export async function createRole(value: RoleModel) {
  const hasSameName = await prisma.role.findFirst({
    where: {
      name: value.name
    }
  })
  if (hasSameName) {
    return 1;
  }

  const permissions: Prisma.RolesPermissionsUncheckedCreateWithoutRoleInput[] = []
  value.permissionIds?.forEach(id => {
    permissions.push({
      permission_id: id,
      assigned_at: new Date(),
    });
  });

  value.permissionIds = undefined;
  await prisma.role.create({
    data: {
      ...value,
      id: undefined,
      status: 1,
      created_at: new Date(),
      permissions: {
        create: permissions
      },
      users: undefined
    }
  });
  return 0;
}

export async function updateRole(value: RoleModel) {
  const hasSameName = await prisma.role.findFirst({
    where: {
      name: value.name,
      id: {
        not: value.id
      }
    }
  });
  if (hasSameName) {
    return 1;
  }

  const permissions: Prisma.RolesPermissionsUncheckedCreateWithoutRoleInput[] = []
  value.permissionIds?.forEach(id => {
    permissions.push({
      permission_id: id,
      assigned_at: new Date(),
    });
  });

  value.permissionIds = undefined;
  await prisma.role.update({
    where: {
      id: value.id
    },
    data: {
      ...value,
      updated_at: new Date(),
      permissions: {
        deleteMany: {},
        create: permissions
      },
      users: undefined
    }
  });
  return 0;
}

export async function deleteRole(id: number) {
  const hasUsers = await prisma.role.findFirst({
    include: { users: true },
  });
  if (hasUsers?.users.length) {
    return false;
  }
  await prisma.rolesPermissions.deleteMany({
    where: { role_id: id },
  });
  await prisma.role.delete({
    where: { id },
  });
  return true;
}